package com.iris.live.services.report.data;

import com.google.common.base.Strings;
import com.iris.live.services.common.CloneUtils;
import com.iris.live.services.common.NumberUtils;
import com.iris.live.services.common.StringUtils;
import com.iris.live.services.data.models.ReportConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;

/**
 * @Description:报表数据转换基础类
 * @author: Tony.Zhang
 * @Date: 2017-08-12 13:40
 **/
public abstract class ReportDataConvertor<T extends ReportContext> implements IReportDataConvertor<T> {
    private static Logger logger = LoggerFactory.getLogger(ReportDataConvertor.class);

    private static DecimalFormat df_int = new DecimalFormat("#0");
    private static DecimalFormat df_ratio = new DecimalFormat("#0.0");
    private static DecimalFormat df_ratio_2 = new DecimalFormat("#0.00");
    private static DecimalFormat df_money = new DecimalFormat("#,##0.00");

    @Override
    public List<Map> convert(T context, ReportConfig config, List<Map> resultset) throws Exception {
        if (context.getDynamicIndicatorLevel() > 0) {
            this.addReportConfig(1, context, config);
        }

        List<ReportConfig> allConfigs = new ArrayList<>();
        this.getListConfigs(allConfigs, config);

        List<Map> results = this.doConvert(context, allConfigs, resultset);
        formatValue(results);
        return results;
    }

    @Override
    public List<Map> convert(T context, List<ReportConfig> configs, List<Map> resultset) throws Exception {
        List<ReportConfig> allConfigs = new ArrayList<>();
        for (ReportConfig config : configs) {
            if (context.getDynamicIndicatorLevel() > 0) {
                this.addReportConfig(1, context, config);
            }

            this.getListConfigs(allConfigs, config);
        }

        List<Map> results = this.doConvert(context, allConfigs, resultset);
        formatValue(results);
        return results;
    }

    /**
     * 数据格式化输出
     * @param values
     */
    private void formatValue(List<Map> values){
        String dataType, valueKey;
        double value ;
        int dataPrecious;

        for(Map resultCell : values) {
            if(StringUtils.isNullOrBlank(resultCell.get("dataType")) || StringUtils.isNullOrBlank(resultCell.get("valueKey"))){
                continue;
            }

            dataType = resultCell.get("dataType").toString();
            valueKey = resultCell.get("valueKey").toString();
            value = StringUtils.isNullOrBlank(resultCell.get(valueKey)) ? 0 : Double.parseDouble(resultCell.get(valueKey).toString());
            dataPrecious = resultCell.get("dataPrecious") == null ? 2 : Integer.parseInt(resultCell.get("dataPrecious").toString());

            if ("Ratio".equalsIgnoreCase(dataType)) {
                //率格式
                if(dataPrecious == 2){
                    resultCell.put(valueKey, df_ratio_2.format(NumberUtils.round(value * 100,2)) + "%");
                }else{
                    resultCell.put(valueKey, df_ratio.format(NumberUtils.round(value * 100,1)) + "%");
                }
            } else if ("Money".equalsIgnoreCase(dataType)) {
                //钱格式
                resultCell.put(valueKey, df_money.format(NumberUtils.round(value, 2)));
            } else if ("INT".equalsIgnoreCase(dataType)) {
                //整型格式
                resultCell.put(valueKey, df_int.format(NumberUtils.roundInt(value)));
            } else {
                //浮点格式
                resultCell.put(valueKey, String.format("%s", NumberUtils.round(value, dataPrecious)));
            }

            resultCell.remove("dataType");
            resultCell.remove("valueKey");
            resultCell.remove("dataPrecious");
        }
    }

    /**
     * @param context
     * @param configs
     * @param resultset
     * @return
     * @throws Exception
     */
    protected List<Map> doConvert(T context, List<ReportConfig> configs, List<Map> resultset) throws Exception {
        List<Map> results = new ArrayList<>();
        Map<String, Map> all = new HashMap<>();
        Map<String, Double> totals = new HashMap<>();
        int status = resultset == null || resultset.isEmpty() ? 0 : 1;

        Collections.sort(configs, new Comparator<ReportConfig>() {
            @Override
            public int compare(ReportConfig o1, ReportConfig o2) {
                if (o1.getPriority() == null && o2.getPriority() == null) {
                    return 0;
                }

                if (o1.getPriority() == null || o2.getPriority() == null) {
                    return -1;
                }

                return o1.getPriority().compareTo(o2.getPriority());
            }
        });
        IReportDataMatcher matcher = context.getMatcher();
        IReportRowHandler handler = context.getHandler();

        long startTime = System.currentTimeMillis();
        //具体统计指标遍历
        List<Map> resultSet4ReportRow = new ArrayList<>();
        for (int index = 0; index < configs.size(); index++) {
            ReportConfig config = configs.get(index);
            if (!config.isFormular()) {
                //根据纬度初始化报表行
                Map<String, Map> reportRow = initReportRow(context, config, status);

                //针对每个指标进行数据遍历
                Double dValue = null;
                resultSet4ReportRow.clear();
                for (Map dataRow : resultset) { //数据直接从数据库获取的
                    if (matcher.isMatched(dataRow, config)) { //判断当前记录是否满足X轴统计条件
                        //获取行数据
                        this.getData(context, reportRow, dataRow, config);

                        resultSet4ReportRow.add(dataRow);
                    }
                }

                //行数据放入报表数据集合
                all.putAll(reportRow);

                //累加
                if (context.isRatio()){
                    for (Object key : reportRow.keySet()) {
                        String strKey = key.toString();
                        strKey = strKey.substring(strKey.lastIndexOf("_") + 1);

                        dValue = Double.valueOf(((Map) reportRow.get(key)).get(config.getCategory() + "Value").toString());
                        if (totals.containsKey(strKey)) {
                            totals.put(strKey, totals.get(strKey) + dValue);
                        } else {
                            totals.put(strKey, dValue);
                        }
                    }
                }

                if(handler != null){
                    handler.handle(reportRow, config, context, resultSet4ReportRow, all);
                }




/*                //后期在表中设置是否输出字段。
                Set<String> keys = reportRow.keySet();
                for(String key : keys){
                    if("1".equals(reportRow.get(key).get("isShown").toString())){
                        results.add((Map)reportRow.get(key));
                    }else{
                        logger.debug("删除项={}", key);
                    }
                }*/


                //将处理数据集添加到返回结果数据集中
                results.addAll(reportRow.values());

            }
        }

        logger.error("time=" + (System.currentTimeMillis() - startTime));
        for (int index = 0; index < configs.size(); index++) {
            ReportConfig config = configs.get(index);
            if (config.isFormular()) {
                Map<String, Map> reportRow = initReportRow(context, config, status);

                if(handler != null && handler.isSupport()){
                    handler.handle(reportRow, config, context, results, all);
                }else{
                    this.getFormularData(context, all, reportRow, config);
                }

                all.putAll(reportRow);

                //后期在表中设置是否输出字段。
 /*               Set<String> keys = reportRow.keySet();
                for(String key : keys){
                    if("1".equals(reportRow.get(key).get("isShown").toString())){
                        results.add((Map)reportRow.get(key));
                    }else{
                        logger.debug("删除项={}", key);
                    }
                }*/

                //将处理数据集添加到返回结果数据集中
                results.addAll(reportRow.values());
            }
        }

        logger.error("time=" + (System.currentTimeMillis() - startTime));

        if (context.isRatio()) { //输出值从合计转换成率
            this.getRatioData(context, results, totals, configs);
        }

        return results;
    }

    /**
     * 初始化报表行
     * @param context
     * @param config
     * @param status
     * @return
     */
    private Map initReportRow(T context,  ReportConfig config, int status){
        String fullPath = this.getFullPath(config);
        Map reportRow = new HashMap<>();
        Map rowCell = null;
        List<? extends ColumnUnit> units = context.getColumnUnits();
        for (ColumnUnit unit : units) {
            rowCell = new HashMap<>();
            rowCell.put("color", 0);
            rowCell.put("displayRow", unit.getText());
            rowCell.put("displayRowOrder", unit.getDisplayRowOrder());
            rowCell.put(config.getCategory() + "Value", Double.valueOf(0));
            rowCell.put("status", status);
            //rowCell.put("isShown", config.getShown());
            if (StringUtils.isNotNullOrBlank(config.getProjectNameOrder())) {
                if(StringUtils.isNotNullOrBlank(context.getOrderName())){
                    rowCell.put(context.getOrderName() + "NameOrder", config.getProjectNameOrder());
                }else if(StringUtils.isNotNullOrBlank(config.getCategory())){
                    if("twoIndex".equalsIgnoreCase(config.getCategory())){
                        rowCell.put("twoIndexNameOrder", config.getProjectNameOrder());
                    }else{
                        rowCell.put("projectNameOrder", config.getProjectNameOrder());
                    }
                }else{
                    rowCell.put("projectNameOrder", config.getProjectNameOrder());
                }
            }

            this.setResultCell(rowCell, config, 0);
            reportRow.put(fullPath + "_" + unit.getValue(), rowCell);
        }

        return reportRow;
    }

    /**
     * 根据用户界面选择动态添加报表配置项
     *
     * @param context
     * @param config
     * @throws Exception
     */
    private void addReportConfig(int index, T context, ReportConfig config) throws Exception {
        int dynamicIndicatorLevel = context.getDynamicIndicatorLevel(), num = 1;
        List<ReportConfig> configs = config.getChildConfigs();

        if (index < dynamicIndicatorLevel - 1) {
            for (ReportConfig subConfig : configs) {
                addReportConfig(++index, context, subConfig);
            }
        } else { //动态添加指标
            ReportConfig existedConfig;
            if (configs.size() >= 2) {
                existedConfig = configs.get(1);
            } else {
                existedConfig = configs.get(0);
            }

            String[] dynamicIndicatorValues = context.getDynamicIndicatorValues().split("[|]");
            for (String dynamicIndicatorValue : dynamicIndicatorValues) {
                if(StringUtils.isNullOrBlank(dynamicIndicatorValue)) {
                    continue;
                }

                ReportConfig newConfig = new ReportConfig();
                String brandCode = new String(dynamicIndicatorValue).replace("+", "_");
                //构造子结点   反序列化构造子结点，保留原结点
                List<ReportConfig> newchildConfig = (List) CloneUtils.deepClone(existedConfig.getChildConfigs());
                for (ReportConfig tempconfig : newchildConfig) {
                    tempconfig.setAinCode(tempconfig.getAinCode().substring(0,tempconfig.getAinCode().lastIndexOf("_") + 1) + brandCode);
                    tempconfig.setParentCode(tempconfig.getParentCode().substring(0, tempconfig.getParentCode().lastIndexOf("_") + 1) + brandCode);

                    tempconfig.setParentConfig(newConfig);
                    if (tempconfig.isFormular()) {
                        String formular = tempconfig.getFormularExpression().replaceAll("(?i)" + existedConfig.getText(), brandCode);
                        tempconfig.setFormularExpression(formular);
                    }
                }
                //构造父节点
                newConfig.setAinCode((existedConfig.getAinCode().replaceAll("(?i)" + existedConfig.getText(), brandCode)));
                newConfig.setCategory(existedConfig.getCategory());
                newConfig.setMatchKey(existedConfig.getMatchKey());
                newConfig.setText(dynamicIndicatorValue);
                newConfig.setParentCode(existedConfig.getParentCode());
                newConfig.setParentConfig(existedConfig.getParentConfig());
                newConfig.setChildConfigs(newchildConfig);
                newConfig.setMatchItem(1);
                newConfig.setProjectNameOrder("0" + num);
                newConfig.setMatcher(1);
                configs.add(newConfig);

                num += 1;
            }
        }
    }

    /**
     * 将报表配置从树状结构转换成列表结构
     *
     * @param parentConfig
     * @return
     */
    private void getListConfigs(List<ReportConfig> configs, ReportConfig parentConfig) {
        List<ReportConfig> children = parentConfig.getChildConfigs();
        if (children != null) {
            for (ReportConfig child : children) {
                if(StringUtils.isNotNullOrBlank(parentConfig.getProjectNameOrder())) {
                    child.setProjectNameOrder(parentConfig.getProjectNameOrder() + child.getProjectNameOrder());
                }
                child.setParentConfig(parentConfig);
                getListConfigs(configs, child);
            }
        } else {
            configs.add(parentConfig);
        }
    }

    /**
     * 设置报表每个单元格的值
     * @param resultCell
     * @param config
     * @param pValue
     */
    protected void updateResultCell(Map resultCell, ReportConfig config, Object pValue) {
        String category = config.getCategory(), valueKey = category + "Value";
        Double value = Double.parseDouble(pValue.toString());

        resultCell.put(valueKey, value);

        resultCell.put("dataType", config.getDataType());
        resultCell.put("dataPrecious", config.getPrecious());
        resultCell.put("valueKey", valueKey);

        ReportConfig rc = config;
        while (true) {
            category = rc.getCategory();
            resultCell.put(category + "Name", rc.getText());
            rc = rc.getParentConfig();
            if (rc == null || Strings.isNullOrEmpty(rc.getText())) {
                break;
            }
        }
    }

    /**
     * 设置报表每个单元格的值
     * @param resultCell
     * @param config
     * @param pValue
     */
    protected void setResultCell(Map resultCell, ReportConfig config, Object pValue) {
        if(pValue == null) {
            return;
        }

        String category = config.getCategory(), valueKey = category + "Value";
        Double value = Double.parseDouble(pValue.toString());

        if(resultCell.get(valueKey)  == null){
            resultCell.put(valueKey, value);
        }else {
            String exists = resultCell.get(valueKey).toString();
            resultCell.put(valueKey, Double.parseDouble(exists) + value);
        }

        resultCell.put("dataType", config.getDataType());
        resultCell.put("dataPrecious", config.getPrecious());
        resultCell.put("valueKey", valueKey);

        ReportConfig rc = config;
        while (true) {
            category = rc.getCategory();
            resultCell.put(category + "Name", rc.getText());
            rc = rc.getParentConfig();
            if (rc == null || Strings.isNullOrEmpty(rc.getText())) {
                break;
            }
        }
    }

    /**
     * 获取某个配置节点在树状结构中的全路径。
     *
     * @param config
     * @return
     */
    protected String getFullPath(ReportConfig config) {
        return config.getAinCode();
    }

    /**
     * 将原始数据转换成报表数据
     * @param context
     * @param reportRow
     * @param resultSet
     * @param config
     * @throws Exception
     */
    protected abstract void getData(T context, Map reportRow, Map resultSet, ReportConfig config) throws Exception;

    /**
     * 获取公式行报表数据
     * @param context
     * @param datas
     * @param reportRow
     * @param config
     * @throws Exception
     */
    protected abstract void getFormularData(T context, Map<String, Map> datas, Map reportRow, ReportConfig config) throws Exception;

    /**
     * 转换成率
     *
     * @param context
     * @param totals
     * @throws Exception
     */
    protected void getRatioData(T context, List<Map> results, Map<String, Double> totals, List<ReportConfig> configs) throws Exception {
        Object total = null;
        String displayRow = null;
        ReportConfig config = configs.get(0);
        for (Map result : results) {
            displayRow = result.get("displayRow").toString(); //获取所属列
            total = totals.get(displayRow); //获取按列合计值
            if (total == null || Math.abs(Double.parseDouble(total.toString())) == 0) {
                result.put("value", 0 + "%");
            } else {
                Double d1 = Double.valueOf(result.get(config.getCategory() + "Value").toString());
                Double d2 = Double.valueOf(total.toString());
                Double d3 = d1 / d2;
                NumberFormat nf = NumberFormat.getPercentInstance();
                nf.setMaximumFractionDigits(1);
                result.put("value", nf.format(d3));
            }
        }
    }
}